package library.database;

import java.io.File;
import java.lang.reflect.Array;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Date;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * @author Dmitry
 */
public class AbstractDatabase {
    private static Connection connection;

    public static abstract class DBMultpCallback {
        public abstract Object getResults(ResultSet rs) throws SQLException;
    };

    public static String getPath() {
        return "./myrulib.db";
    }

    public static boolean init() {
        try {
            Class.forName("org.sqlite.JDBC");
            if(!(new File(getPath())).exists()){
                throw new RuntimeException("Cannot find database file with path "+getPath());
            }
            connection = DriverManager.getConnection("jdbc:sqlite:" + getPath());
            return true;
        } catch (SQLException ex) {
            System.err.println("Cannot load book database");
            Logger.getLogger(AbstractDatabase.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ClassNotFoundException ex) {
            System.err.println("Cannot load jdbc sqlite driver");
            Logger.getLogger(AbstractDatabase.class.getName()).log(Level.SEVERE, null, ex);
        }
        return false;
    }

    protected static Object SQLReturnObject(String sql, Map<Integer, Object> params, DBMultpCallback callback) {
        Object[] results = SQLReturnObjects(sql, params, callback);
        if (results == null || results.length == 0) {
            return null;
        }
        return results[0];
    }

    protected static Object[] SQLReturnObjects(String sql, Map<Integer, Object> params, DBMultpCallback callback) {
        ResultSet rs = null;
        PreparedStatement statement = null;
        try {
            System.out.println("Start "+System.currentTimeMillis()/1000);
            statement = connection.prepareStatement(sql);
            if (params != null) {
                for (Integer key : params.keySet()) {
                    Object value = params.get(key);
                    if (value instanceof Float) {
                        statement.setFloat(key, (Float) value);
                    } else if (value instanceof Double) {
                        statement.setDouble(key, (Double) value);
                    } else if (value instanceof String) {
                        statement.setString(key, (String) value);
                    } else if (value instanceof Integer) {
                        statement.setInt(key, (Integer) value);
                    } else if (value instanceof Date) {
                        statement.setDate(key, new java.sql.Date(((Date) value).getTime()));
                    } else if (value instanceof java.sql.Date) {
                        statement.setDate(key, (java.sql.Date) value);
                    } else if (value instanceof Boolean) {
                        statement.setBoolean(key, (Boolean) value);
                    }
                }
            }
            if (statement.execute()) {
                rs = statement.getResultSet();
                System.out.println("Fetch "+System.currentTimeMillis()/1000);
                if (callback != null) {
                    ArrayList objects = new ArrayList();
                    while (rs.next()) {
                        Object obj = callback.getResults(rs);
                        objects.add(obj);
                    }
                    System.out.println("End "+System.currentTimeMillis()/1000);
                    if (objects.isEmpty()) {
                        return null;
                    } else {
                        return objects.toArray((Object[]) Array.newInstance(objects.get(0).getClass(), 0));
                    }

                }
            }
        } catch (SQLException ex) {
            Logger.getLogger(AbstractDatabase.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException ex) {
                    Logger.getLogger(AbstractDatabase.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            if (statement != null) {
                try {
                    statement.close();
                } catch (SQLException ex) {
                    Logger.getLogger(AbstractDatabase.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
        return null;
    }
}
